
/*
    Software License

    Copyright (C) 2021-05-24  Xoronos

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

/*
    Liabilities

    The software is provided "AS IS" without any warranty of any kind, either expressed,
    implied, or statutory, including, but not limited to, any warranty that the software
    will conform to specifications, any implied warranties of merchantability, fitness
    for a particular purpose, and freedom from infringement, and any warranty that the
    documentation will conform to the software, or any warranty that the software will
    be error free.

    In no event shall Xoronos be liable for any damages, including, but not limited to,
    direct, indirect, special or consequential damages, arising out of, resulting from,
    or in any way connected with this software, whether or not based upon warranty,
    contract, tort, or otherwise, whether or not injury was sustained by persons or
    property or otherwise, and whether or not loss was sustained from, or arose out of
    the results of, or use of, the software or services provided hereunder.

    To request the provided software under a different license you can contact us at
    support@xoronos.com
*/

#include "test_encoder.h"
#define XRN_MAX_LENGTH_ENCODING_BUFFER 4096

int test_encoder_init(
 )
{

    xrn_encoding_param_t enc_param0, enc_param1, enc_param2, enc_param3;
    int i, error;
    xrn_encoding_settings_t encoding_settings;
    char c;
    FILE *fp, *initialization_file;
    char encoder_str[256];
    char encoder_str2[256];

    strcpy( encoder_str, "../cache/to_remove_encoder.txt" );
    strcpy( encoder_str2, "../cache/to_remove_init.txt" );

    for ( i = 0; i < XRN_ENC_PASS_BUFF_SIZE; i++ ) {
        encoding_settings.password[i] = ( char ) ( rand(  ) % 256 );
    }

    for ( i = 0; i < sizeof( encoding_settings ); i++ ) {
        ( ( uint8_t * ) & encoding_settings )[i] = 0;
    }

    encoding_settings.password_length = XRN_ENC_PASS_BUFF_SIZE;

    // standard parameters
    encoding_settings.mode = std_parameters;
    encoding_settings.check_mode = xrn_automatic;

    if ( XSUCCESS != xrn_initialize_encoding_parameters( &enc_param0, encoding_settings ) ) {
        return 1;

    }

    // dump standard parameters
    fp = fopen( encoder_str, "wb" );
    xrn_dump_encoder_parameters( enc_param0, fp );
    fclose( fp );

    // xrn parameters plus password
    encoding_settings.encoding_parameters_fp = fopen( encoder_str, "rb" );
    encoding_settings.mode = xrn_parameters_plus_password;
    encoding_settings.check_mode = xrn_automatic;

    if ( XSUCCESS != xrn_initialize_encoding_parameters( &enc_param1, encoding_settings ) ) {

        remove( encoder_str );
        return 1;

    }

    fclose( encoding_settings.encoding_parameters_fp );

    // std_parameters_plus_password

    encoding_settings.mode = std_parameters_plus_password;
    encoding_settings.check_mode = xrn_automatic;

    if ( XSUCCESS != xrn_initialize_encoding_parameters( &enc_param2, encoding_settings ) ) {

        remove( encoder_str );
        return 1;

    }

    if ( XNOT_EQUAL == xrn_compare_encoding_parameters( enc_param1, enc_param2 ) ) {

        remove( encoder_str );
        return 1;

    }

    error = 0;
    for ( i = 0; i < 256; i++ ) {
        error = error || ( enc_param0.table[i] != STD_ENC_PARAM_TABLE[i] );
    }

    for ( i = 0; i < 8; i++ ) {
        error = error || ( ( ( uint32_t * ) & enc_param0.prng_state[0] )[i] != STD_ENC_PARAM_CNT[i] );
    }

    if ( error ) {

        remove( encoder_str );
        return 1;

    }

    // xrn_parameters

    encoding_settings.encoding_parameters_fp = fopen( encoder_str, "rb" );
    encoding_settings.mode = xrn_parameters;
    encoding_settings.check_mode = xrn_automatic;

    if ( XSUCCESS != xrn_initialize_encoding_parameters( &enc_param3, encoding_settings ) ) {

        return 1;

    }

    fclose( encoding_settings.encoding_parameters_fp );

    if ( XNOT_EQUAL == xrn_compare_encoding_parameters( enc_param3, enc_param0 ) ) {

        remove( encoder_str );
        return 1;

    }

    // file encoding paramethers

    initialization_file = fopen( encoder_str2, "wb" );
    if ( initialization_file == NULL ) {
        remove( encoder_str );
        return 1;
    }

    for ( i = 0; i < 256 * 13; i++ ) {
        c = ( char ) ( rand(  ) % 256 );
        if ( 1 != fwrite( &c, 1, 1, initialization_file ) ) {

            fclose( fp );
            remove( encoder_str );
            return 1;

        }

    }

    fclose( initialization_file );

    initialization_file = fopen( encoder_str2, "rb" );
    if ( initialization_file == NULL ) {
        remove( encoder_str );
        remove( encoder_str2 );
        return 1;
    }

    // xrn parameters plus file
    encoding_settings.encoding_parameters_fp = fopen( encoder_str, "rb" );
    encoding_settings.initialization_file_fp = initialization_file;
    encoding_settings.mode = xrn_parameters_plus_file;
    encoding_settings.check_mode = xrn_automatic;
    if ( XSUCCESS != xrn_initialize_encoding_parameters( &enc_param1, encoding_settings ) ) {

        remove( encoder_str );
        remove( encoder_str2 );
        fclose( initialization_file );
        return 1;

    }

    fclose( encoding_settings.encoding_parameters_fp );

    // std_parameters_plus_file

    rewind( initialization_file );
    encoding_settings.mode = std_parameters_plus_file;
    encoding_settings.initialization_file_fp = initialization_file;
    encoding_settings.check_mode = xrn_automatic;

    if ( XSUCCESS != xrn_initialize_encoding_parameters( &enc_param2, encoding_settings ) ) {

        remove( encoder_str );
        remove( encoder_str2 );
        fclose( initialization_file );
        return 1;

    }

    if ( XNOT_EQUAL == xrn_compare_encoding_parameters( enc_param1, enc_param2 ) ) {

        remove( encoder_str );
        remove( encoder_str2 );
        fclose( initialization_file );
        return 1;

    }

    fclose( initialization_file );
    remove( encoder_str2 );
    remove( encoder_str );

    return 0;

}

int test_encoder_encryption(
 )
{

    xrn_encoding_param_t enc_param;
    xrn_encoding_tmp_param_t enc_tmp_param;
    int i, error;
    uint64_t length;
    uint8_t in_bytes[XRN_MAX_LENGTH_ENCODING_BUFFER];
    uint8_t enc_bytes[XRN_MAX_LENGTH_ENCODING_BUFFER];
    uint8_t dec_bytes[XRN_MAX_LENGTH_ENCODING_BUFFER];
    xrn_encoding_settings_t encoding_settings;

    length = ( uint64_t ) ( rand(  ) % XRN_MAX_LENGTH_ENCODING_BUFFER );
    for ( i = 0; i < XRN_MAX_LENGTH_ENCODING_BUFFER; i++ ) {
        in_bytes[i] = ( uint8_t ) ( rand(  ) % 256 );
    }

    xrn_initialize_encoding_settings( &encoding_settings );
    xrn_initialize_encoding_parameters( &enc_param, encoding_settings );

    if ( xrn_encode( &enc_param, in_bytes, enc_bytes, length, &enc_tmp_param ) != XSUCCESS ) {

        return 1;

    }

    xrn_initialize_encoding_parameters( &enc_param, encoding_settings );

    if ( xrn_decode( &enc_param, enc_bytes, dec_bytes, length, &enc_tmp_param ) != XSUCCESS ) {

        return 1;

    }

    error = 0;

    for ( i = 0; i < length; i++ ) {

        error = error || ( dec_bytes[i] != in_bytes[i] );

    }

    if ( error ) {

        return 1;

    }

    if ( XSUCCESS != xrn_check_encoding_parameters( enc_param ) ) {
        return 1;

    }

    return 0;

}

int test_encoder_read_write(
 )
{

    int i, error;
    uint8_t buff0[XRN_MAX_LENGTH_ENCODING_BUFFER];
    uint8_t buff1[XRN_MAX_LENGTH_ENCODING_BUFFER];

    xrn_encoding_param_t encoding_param;
    xrn_encoding_settings_t encoding_settings;

    FILE *fp;
    char encoder_str[256];

    strcpy( encoder_str, "../cache/to_remove_encoder.txt" );

    for ( i = 0; i < XRN_MAX_LENGTH_ENCODING_BUFFER; i++ ) {
        buff0[i] = ( uint8_t ) ( rand(  ) % 256 );
    }

    for ( i = 0; i < sizeof( encoding_settings ); i++ ) {
        ( ( uint8_t * ) & encoding_settings )[i] = 0;
    }

    encoding_settings.mode = std_parameters;
    encoding_settings.check_mode = xrn_automatic;

    if ( XSUCCESS != xrn_initialize_encoding_parameters( &encoding_param, encoding_settings ) ) {

        return 1;

    }

    fp = fopen( encoder_str, "wb" );
    if ( 1 != xrn_fwrite_encoder( buff0, XRN_MAX_LENGTH_ENCODING_BUFFER, 1, fp, &encoding_param ) ) {

        fclose( fp );
        remove( encoder_str );
        return 1;

    }

    fclose( fp );

    encoding_settings.mode = std_parameters;
    encoding_settings.check_mode = xrn_automatic;
    if ( XSUCCESS != xrn_initialize_encoding_parameters( &encoding_param, encoding_settings ) ) {

        return 1;

    }

    fp = fopen( encoder_str, "rb" );
    if ( 1 != xrn_fread_decoder( buff1, XRN_MAX_LENGTH_ENCODING_BUFFER, 1, fp, &encoding_param ) ) {

        fclose( fp );
        remove( encoder_str );
        return 1;

    }

    fclose( fp );

    // check
    error = 0;
    for ( i = 0; i < XRN_MAX_LENGTH_ENCODING_BUFFER; i++ ) {
        error = error || ( buff0[i] != buff1[i] );
    }

    if ( error ) {
        remove( encoder_str );
        return 1;
    }

    encoding_settings.mode = std_parameters;
    encoding_settings.check_mode = xrn_automatic;
    if ( XSUCCESS != xrn_initialize_encoding_parameters( &encoding_param, encoding_settings ) ) {

        remove( encoder_str );
        return 1;

    }

    fp = fopen( encoder_str, "wb" );
    if ( XRN_MAX_LENGTH_ENCODING_BUFFER !=
         xrn_fwrite_encoder( buff0, 1, XRN_MAX_LENGTH_ENCODING_BUFFER, fp, &encoding_param ) ) {

        fclose( fp );
        remove( encoder_str );
        return 1;

    }

    fclose( fp );

    encoding_settings.mode = std_parameters;
    encoding_settings.check_mode = xrn_automatic;
    if ( XSUCCESS != xrn_initialize_encoding_parameters( &encoding_param, encoding_settings ) ) {

        remove( encoder_str );
        return 1;

    }

    fp = fopen( encoder_str, "rb" );
    if ( XRN_MAX_LENGTH_ENCODING_BUFFER !=
         xrn_fread_decoder( buff1, 1, XRN_MAX_LENGTH_ENCODING_BUFFER, fp, &encoding_param ) ) {

        fclose( fp );
        remove( encoder_str );
        return 1;

    }

    fclose( fp );
    remove( encoder_str );

    // check
    error = 0;
    for ( i = 0; i < XRN_MAX_LENGTH_ENCODING_BUFFER; i++ ) {
        error = error || ( buff0[i] != buff1[i] );
    }

    if ( error ) {

        return 1;
    }

    return 0;
}

int test_prob_error(
 )
{

    xrn_encoding_param_t enc_param;
    xrn_encoding_tmp_param_t enc_tmp_param;
    xrn_encoding_settings_t encoding_settings;
    int i, ret;
    uint8_t in_bytes[XRN_MAX_LENGTH_ENCODING_BUFFER];

    xrn_change_warning_log( NULL, 0 );

    xrn_initialize_encoding_settings( &encoding_settings );
    encoding_settings.check_mode = xrn_manual;
    encoding_settings.mode = no_encoding;

    // check running error 0

    encoding_settings.check_running_error_en = 1;
    encoding_settings.check_disparity_error_en = 0;

    xrn_initialize_encoding_parameters( &enc_param, encoding_settings );

    for ( i = 0; i < XRN_MAX_LENGTH_ENCODING_BUFFER; i++ ) {
        in_bytes[i] = 0;
    }

    ret = xrn_encode( &enc_param, in_bytes, in_bytes, XRN_MAX_LENGTH_ENCODING_BUFFER, &enc_tmp_param );
    if ( ret != XERR_PROB_ENC_C0 ) {

        xrn_change_warning_log( XSTD_WARNING_OUTPUT, 1 );
        return 1;

    }

    // check avg error 0

    encoding_settings.check_running_error_en = 0;
    encoding_settings.check_disparity_error_en = 1;

    xrn_initialize_encoding_parameters( &enc_param, encoding_settings );

    ret = xrn_encode( &enc_param, in_bytes, in_bytes, XRN_MAX_LENGTH_ENCODING_BUFFER, &enc_tmp_param );

    if ( ret != XERR_PROB_ENC_0 ) {

        xrn_change_warning_log( XSTD_WARNING_OUTPUT, 1 );
        return 1;

    }

    // check running error 1

    encoding_settings.check_running_error_en = 1;
    encoding_settings.check_disparity_error_en = 0;

    xrn_initialize_encoding_parameters( &enc_param, encoding_settings );

    for ( i = 0; i < XRN_MAX_LENGTH_ENCODING_BUFFER; i++ ) {
        in_bytes[i] = 255;
    }

    ret = xrn_encode( &enc_param, in_bytes, in_bytes, XRN_MAX_LENGTH_ENCODING_BUFFER, &enc_tmp_param );

    if ( ret != XERR_PROB_ENC_C1 ) {

        xrn_change_warning_log( XSTD_WARNING_OUTPUT, 1 );
        return 1;

    }

    // check avg error 1

    encoding_settings.check_running_error_en = 0;
    encoding_settings.check_disparity_error_en = 1;

    xrn_initialize_encoding_parameters( &enc_param, encoding_settings );

    ret = xrn_encode( &enc_param, in_bytes, in_bytes, XRN_MAX_LENGTH_ENCODING_BUFFER, &enc_tmp_param );

    if ( ret != XERR_PROB_ENC_1 ) {

        xrn_change_warning_log( XSTD_WARNING_OUTPUT, 1 );
        return 1;

    }

    xrn_change_warning_log( XSTD_WARNING_OUTPUT, 1 );
    return 0;

}

int test_encoder_all( FILE * fp, int runs, int non_block
 )
{
    time_t t;
    char buff[256];
    int v_test_encoder_encryption;
    int v_test_encoder_init;
    int v_test_encoder_read_write;
    int v_test_prob_error;

    srand( ( unsigned ) time( &t ) );
    int error;
    int occ;

    error = 0;

    for ( occ = 0; ( occ < runs ) && ( ( error == 0 ) || non_block ); occ++ ) {
        srand( ( unsigned ) time( &t ) + ( unsigned ) occ );
        v_test_encoder_init = test_encoder_init(  );
        error += v_test_encoder_init;
        printf( "[%4d]", occ );
        if ( !v_test_encoder_init ) {
            printf( "test_encoder -> test_encoder_init passed\n" );
            fprintf( fp, "test_encoder -> test_encoder_init passed\n" );
        } else {
            printf( "test_encoder -> test_encoder_init failed\n" );
            fprintf( fp, "test_encoder -> test_encoder_init failed\n" );
        }

        if ( v_test_encoder_init != 0 ) {
#if ( XRN_GNU_LINUX == 1 )
            sprintf( buff, "TEST_ENCODER FAILED SEED %ld ", t );
            fprintf( fp, "TEST_ENCODER FAILED SEED %ld \n", t );
#else
            sprintf( buff, "TEST_ENCODER FAILED SEED %lld ", t );
            fprintf( fp, "TEST_ENCODER FAILED SEED %lld \n", t );
#endif
            printf( "%s", buff );
        }

    }

    for ( occ = 0; ( occ < runs ) && ( ( error == 0 ) || non_block ); occ++ ) {
        srand( ( unsigned ) time( &t ) + ( unsigned ) occ );
        v_test_prob_error = test_prob_error(  );
        error += v_test_prob_error;
        printf( "[%4d]", occ );
        if ( !v_test_prob_error ) {
            printf( "test_encoder -> negative test_prob_error passed\n" );
            fprintf( fp, "test_encoder -> negative test_prob_error passed\n" );
        } else {
            printf( "test_encoder -> negative test_prob_error failed\n" );
            fprintf( fp, "test_encoder -> negative test_prob_error failed\n" );
        }

        if ( v_test_prob_error != 0 ) {
#if ( XRN_GNU_LINUX == 1 )
            sprintf( buff, "TEST_ENCODER FAILED SEED %ld ", t );
            fprintf( fp, "TEST_ENCODER FAILED SEED %ld \n", t );
#else
            sprintf( buff, "TEST_ENCODER FAILED SEED %lld ", t );
            fprintf( fp, "TEST_ENCODER FAILED SEED %lld \n", t );
#endif
            printf( "%s", buff );
        }

    }

    for ( occ = 0; ( occ < runs ) && ( ( error == 0 ) || non_block ); occ++ ) {
        srand( ( unsigned ) time( &t ) + ( unsigned ) occ );
        v_test_encoder_read_write = test_encoder_read_write(  );
        error += v_test_encoder_read_write;
        printf( "[%4d]", occ );
        if ( !v_test_encoder_read_write ) {
            printf( "test_encoder -> test_encoder_read_write passed\n" );
            fprintf( fp, "test_encoder -> test_encoder_read_write passed\n" );
        } else {
            printf( "test_encoder -> test_encoder_read_write failed\n" );
            fprintf( fp, "test_encoder -> test_encoder_read_write failed\n" );
        }

        if ( v_test_encoder_read_write != 0 ) {
#if ( XRN_GNU_LINUX == 1 )
            sprintf( buff, "TEST_ENCODER FAILED SEED %ld ", t );
            fprintf( fp, "TEST_ENCODER FAILED SEED %ld \n", t );
#else
            sprintf( buff, "TEST_ENCODER FAILED SEED %lld ", t );
            fprintf( fp, "TEST_ENCODER FAILED SEED %lld \n", t );
#endif
            printf( "%s", buff );
        }

    }

    for ( occ = 0; ( occ < runs ) && ( ( error == 0 ) || non_block ); occ++ ) {
        srand( ( unsigned ) time( &t ) + ( unsigned ) occ );
        v_test_encoder_encryption = test_encoder_encryption(  );
        error += v_test_encoder_encryption;
        printf( "[%4d]", occ );
        if ( !v_test_encoder_encryption ) {
            printf( "test_encoder -> test_encoder_encryption passed\n" );
            fprintf( fp, "test_encoder -> test_encoder_encryption passed\n" );
        } else {
            printf( "test_encoder -> test_encoder_encryption failed\n" );
            fprintf( fp, "test_encoder -> test_encoder_encryption failed\n" );
        }

        if ( v_test_encoder_encryption != 0 ) {
#if ( XRN_GNU_LINUX == 1 )
            sprintf( buff, "TEST_ENCODER FAILED SEED %ld ", t );
            fprintf( fp, "TEST_ENCODER FAILED SEED %ld \n", t );
#else
            sprintf( buff, "TEST_ENCODER FAILED SEED %lld ", t );
            fprintf( fp, "TEST_ENCODER FAILED SEED %lld \n", t );
#endif
            printf( "%s", buff );
        }

    }

    return error;
}

